package com.inno.subject.domain.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.inno.lock.processor.annotiation.DistributedLock;
import com.inno.subject.common.entity.PageResult;
import com.inno.subject.common.enums.IsDeletedFlagEnum;
import com.inno.subject.common.enums.SubjectLevelEnum;
import com.inno.subject.common.utils.DateUtils;
import com.inno.subject.domain.convert.SubjectCollectConverter;
import com.inno.subject.domain.entity.SubjectCollectBO;
import com.inno.subject.domain.service.SubjectCollectDomainService;
import com.inno.subject.infra.basic.entity.PlannerSubjectCategory;
import com.inno.subject.infra.basic.entity.PlannerSubjectCollect;
import com.inno.subject.infra.basic.entity.PlannerSubjectInfo;
import com.inno.subject.infra.basic.entity.PlannerSubjectType;
import com.inno.subject.infra.basic.service.PlannerSubjectCategoryService;
import com.inno.subject.infra.basic.service.PlannerSubjectCollectService;
import com.inno.subject.infra.basic.service.PlannerSubjectInfoService;
import com.inno.subject.infra.basic.service.PlannerSubjectTypeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author sean
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SubjectCollectDomainServiceImpl implements SubjectCollectDomainService {
    private final PlannerSubjectCollectService plannerSubjectCollectService;
    private final PlannerSubjectInfoService plannerSubjectInfoService;
    private final ThreadPoolTaskExecutor subjectExecutor;
    private final PlannerSubjectCategoryService plannerSubjectCategoryService;
    private final PlannerSubjectTypeService plannerSubjectTypeService;
    private static final ReentrantLock LOCK = new ReentrantLock();

    /**
     * 收藏
     */
    @Override
    @DistributedLock(key = "subject_collect",expireTime = 2,timeUnit = TimeUnit.SECONDS)
    public void add(SubjectCollectBO subjectInfoBO) {
        Long userId = subjectInfoBO.getCurrentUser();
        Long subjectId = subjectInfoBO.getSubjectId();
        PlannerSubjectCollect plannerSubjectCollect = new PlannerSubjectCollect();
        plannerSubjectCollect.setSubjectId(subjectId);
        plannerSubjectCollect.setCreateBy(userId);
        plannerSubjectCollect.setUserId(userId);
        plannerSubjectCollect.setId(IdUtil.getSnowflakeNextId());

        PlannerSubjectCollect collect4db = this.plannerSubjectCollectService.queryBySubjectIdAndUserId(plannerSubjectCollect);
        if (Objects.nonNull(collect4db)){
            this.plannerSubjectCollectService.deleteById(collect4db.getId());
            return;
        }
        this.processorEntity(subjectId, plannerSubjectCollect);
        this.plannerSubjectCollectService.insert(plannerSubjectCollect);


       // 单机情况下 ReentrantLock 没有问题  但是如果是多节点部署 ReentrantLock就不行了 需要分布式锁
        /*LOCK.lock();
        try {
            PlannerSubjectCollect collect4db = this.plannerSubjectCollectService.queryBySubjectIdAndUserId(plannerSubjectCollect);
            if (Objects.nonNull(collect4db)){
                this.plannerSubjectCollectService.deleteById(collect4db.getId());
                return;
            }
            this.processorEntity(subjectId, plannerSubjectCollect);
            this.plannerSubjectCollectService.insert(plannerSubjectCollect);
        } finally {
            LOCK.unlock();
        }*/
    }

    /**
     * 处理要新增的实体字段信息
     */
    private void processorEntity(Long subjectId, PlannerSubjectCollect plannerSubjectCollect) {
        PlannerSubjectInfo plannerSubjectInfo = this.plannerSubjectInfoService.queryById(subjectId);
        plannerSubjectCollect.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode().toString());
        plannerSubjectCollect.setCreateTime(DateUtils.getNowDate());
        plannerSubjectCollect.setCategoryId(plannerSubjectInfo.getCategoryId());
        plannerSubjectCollect.setSubjectType(plannerSubjectInfo.getSubjectType());
        plannerSubjectCollect.setSubjectLevel(plannerSubjectInfo.getSubjectLevel());
    }


    /**
     * 收藏列表查询
     */
    @Override
    public PageResult<SubjectCollectBO> queryPage(SubjectCollectBO subjectCollectBO) {
        PageResult<SubjectCollectBO> result = new PageResult<>();

        PlannerSubjectCollect plannerSubjectCollect = SubjectCollectConverter.INSTANCE.convertBOToEntity(subjectCollectBO);
        plannerSubjectCollect.setUserId(subjectCollectBO.getCurrentUser());
        PageHelper.startPage(subjectCollectBO.getPage(), subjectCollectBO.getPageSize());
        Page<PlannerSubjectCollect> entityPage = this.plannerSubjectCollectService.queryPage(plannerSubjectCollect);
        if (CollectionUtil.isEmpty(entityPage.getResult())){
            result.setTotal(0L);
            return result;
        }


        List<SubjectCollectBO> bos = SubjectCollectConverter.INSTANCE.convertEntityListToBOList(entityPage.getResult());
        result.setTotal(entityPage.getTotal());

        CompletableFuture<Map<Long, PlannerSubjectInfo>> subjectInfoFuture = CompletableFuture.supplyAsync(() -> {
            List<Long> subjectIdList = bos.stream().map(SubjectCollectBO::getSubjectId).toList();
            return this.plannerSubjectInfoService.queryByIdList(subjectIdList)
                    .stream()
                    .collect(Collectors.toMap(PlannerSubjectInfo::getId, Function.identity()));
        }, subjectExecutor);
        // 查询题目分类名称  类型名称
        CompletableFuture<Map<Long, String>> typeFuture = CompletableFuture.supplyAsync(() -> {
            List<Long> subjectTypeIdList = bos.stream().map(SubjectCollectBO::getSubjectType).toList();

            return this.plannerSubjectTypeService.queryByIdList(subjectTypeIdList)
                    .stream()
                    .collect(Collectors.toMap(
                            PlannerSubjectType::getId, PlannerSubjectType::getTypeName
                    ));

        }, subjectExecutor);
        CompletableFuture<Map<Long, String>> categoryFuture = CompletableFuture.supplyAsync(() -> {
            List<Long> subjectCategoryIdList = bos.stream().map(SubjectCollectBO::getCategoryId).toList();
            return this.plannerSubjectCategoryService.queryByIdList(subjectCategoryIdList)
                    .stream()
                    .collect(Collectors.toMap(
                            PlannerSubjectCategory::getId, PlannerSubjectCategory::getCategoryName
                    ));
        }, subjectExecutor);

        bos.forEach(bo->{
            bo.setSubjectLevelName(SubjectLevelEnum.getDescByCode(bo.getSubjectLevel()));
        });

        Map<Long, String> typeMap = typeFuture.join();
        Map<Long, String> categoryMap = categoryFuture.join();
        Map<Long, PlannerSubjectInfo> subjectInfoMap = subjectInfoFuture.join();
        bos.forEach(bo -> {
            PlannerSubjectInfo plannerSubjectInfo = subjectInfoMap.get(bo.getSubjectId());
            bo.setSubjectTypeName(typeMap.get(bo.getSubjectType()));
            bo.setCategoryName(categoryMap.get(bo.getCategoryId()));
            bo.setSubjectName(plannerSubjectInfo.getSubjectName());
        });

        result.setList(bos);
        return result;
    }
}
